/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "turbo_trans_protobuf_util.h"
#include "google/protobuf/compiler/command_line_interface.h"
#include "google/protobuf/util/message_differencer.h"
#include "google/protobuf/dynamic_message.h"
#include "protobuf_util.h"
#include "napi_util.h"
#include <cstdint>
#include <string>

using namespace protobuf_util;

napi_value turbo_trans_protobuf_util::Decode(napi_env env, napi_callback_info info) {
  size_t argc = 3;
  napi_value args[3] = {};
  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

  napi_valuetype type;
  napi_typeof(env, args[0], &type);

  if (type != napi_object) {
    napi_throw_error(env, nullptr, "param must be an object");
  }
  void *dataPtr = nullptr;
  size_t dataLength = 0;
  napi_get_typedarray_info(env, args[0], nullptr, &dataLength, &dataPtr, nullptr, nullptr);

  void *descPtr = nullptr;
  size_t descLength = 0;
  napi_get_typedarray_info(env, args[1], nullptr, &descLength, &descPtr, nullptr, nullptr);

  std::string namespaceStr;
  NapiUtil::GetStringUtf8(env, args[2], &namespaceStr);

  uint8_t *binaryDateDesc = static_cast<uint8_t *>(descPtr);
  google::protobuf::FileDescriptorSet descriptor_set;
  bool success = descriptor_set.ParseFromArray(binaryDateDesc, descLength);
  google::protobuf::DescriptorPool &desc_pool = protobuf_util::getGlobalDescPool();

  // TODO 是否每次都需要build
  for (const auto &file_desc : descriptor_set.file()) {
    desc_pool.BuildFile(file_desc);
  }

  const google::protobuf::Descriptor *msg_desc = desc_pool.FindMessageTypeByName(namespaceStr);
  if (!msg_desc) {
    return nullptr;
  }
  const google::protobuf::Message *prototype =
      protobuf_util::DynamicMessageFactorySingleton::GetInstance()->GetPrototype(msg_desc);
  google::protobuf::Message *msg = prototype->New();
  protobuf_util::ConverterSingleton &instance = protobuf_util::ConverterSingleton::getInstance();
  napi_value res;
  instance.MessageToJs(env, msg, res);
  return res;
}


napi_value turbo_trans_protobuf_util::Encode(napi_env env, napi_callback_info info) {
  size_t argc = 3;
  napi_value args[3] = {};
  LOGI("encode begin");
  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);


  void *descPtr = nullptr;
  size_t descLength = 0;
  napi_get_typedarray_info(env, args[1], nullptr, &descLength, &descPtr, nullptr, nullptr);

  std::string namespaceStr;
  NapiUtil::GetStringUtf8(env, args[2], &namespaceStr);

  uint8_t *binaryDateDesc = static_cast<uint8_t *>(descPtr);
  google::protobuf::FileDescriptorSet descriptor_set;
  bool success = descriptor_set.ParseFromArray(binaryDateDesc, descLength);
  google::protobuf::DescriptorPool &desc_pool = protobuf_util::getGlobalDescPool();

  for (const auto &file_desc : descriptor_set.file()) {
    desc_pool.BuildFile(file_desc);
  }

  const google::protobuf::Descriptor *msg_desc = desc_pool.FindMessageTypeByName(namespaceStr);
  if (!msg_desc) {
    return nullptr;
  }

  const google::protobuf::Message *prototype =
      protobuf_util::DynamicMessageFactorySingleton::GetInstance()->GetPrototype(msg_desc);
  google::protobuf::Message *msg = prototype->New();
  protobuf_util::ConverterSingleton &instance = protobuf_util::ConverterSingleton::getInstance();

  instance.JsToMessage(env, args[0], msg);
    LOGI("msg is %{public}s", msg->DebugString().c_str());

  size_t serialized_size = msg->ByteSizeLong();
  void *buffer = malloc(sizeof(uint8_t) * serialized_size);
  bool serialize_status = msg->SerializeToArray(buffer, serialized_size);
  if (!serialize_status) {
    // 解析失败
    return nullptr;
  }
  void *arr_buf_ptr = nullptr;
  napi_value arraybuffer = nullptr;
  napi_create_arraybuffer(env, serialized_size, &arr_buf_ptr, &arraybuffer);
  if (arr_buf_ptr != nullptr) {
    memcpy(arr_buf_ptr, buffer, serialized_size);
    return arraybuffer;
  } else {
    return nullptr;
  }
}